package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/jinzhu/gorm"
	"github.com/shopspring/decimal"
	"github.com/smartwalle/alipay"
	"math"
	"rpcSrv/MQ"
	"rpcSrv/SDK/alipay_sdk"
	"rpcSrv/dbs/mysqlGORM"
	"rpcSrv/entitys"
	pb "rpcSrv/proto/integral"
	"rpcSrv/utils"
	"strconv"
	"strings"
	"time"
)

type IntegralHdl struct {
}

func (this *IntegralHdl) Recharge(ctx context.Context, req *pb.RechargeParams) (resp *pb.RespRechargeParams, err error) {
	uid := req.GetUid()
	if uid == 0 {
		return nil, fmt.Errorf("请传入用户ID")
	}
	notify_url := req.GetNotifyUrl()
	return_url := req.GetReturnUrl()
	if notify_url == "" || return_url == "" {
		return nil, fmt.Errorf("notify_url跟return_url不可为空")
	}
	amount := req.GetAmount()
	if amount < 0.01 {
		return nil, fmt.Errorf("最少充值0.01元")
	}
	if amount > 99999 {
		return nil, fmt.Errorf("最多充值99999元")
	}
	mark := req.GetMark()
	if mark == "" {
		mark = "积分充值"
	}
	outTradeNo := fmt.Sprintf("%v_%v", uid, time.Now().UnixNano())
	p := alipay.AliPayTradeWapPay{}
	p.NotifyURL = notify_url
	p.ReturnURL = return_url
	p.Subject = mark
	p.OutTradeNo = outTradeNo
	p.TotalAmount = fmt.Sprintf("%v", amount)
	p.ProductCode = "QUICK_WAP_WAY"
	cli := alipay_sdk.GetAliPayCli()
	url, err := cli.TradeWapPay(p)
	if utils.CheckErr(err) {
		return nil, err
	}
	resp = new(pb.RespRechargeParams)
	resp.Url = url.String()
	return resp, nil
}

func (this *IntegralHdl) Pay(ctx context.Context, req *pb.ReqPayParams) (resp *pb.FlowRecord, err error)  {
	uid := req.GetUid()
	if uid == 0 {
		return nil, fmt.Errorf("请传入用户ID")
	}
	db, err := mysqlGORM.GetMysqlDB()
	if utils.CheckErr(err) {
		return nil, err
	}
	tx := db.Begin()
	versionVO := struct {
		Version int64 `json:"version"`
	}{}
	if notFound := db.Table(new(entitys.User).TableName()).
		Select("version").
		Where("id = ?",uid).
		First(&versionVO).RecordNotFound(); notFound {
			tx.Rollback()
			return nil, fmt.Errorf("用户不存在")
	}
	if num := tx.Table(new(entitys.User).TableName()).
		Where("id = ? and version = ?",uid,versionVO.Version).
		UpdateColumn("version",gorm.Expr("version + ?",1)).
		RowsAffected; num == 0 {
			tx.Rollback()
			return nil, fmt.Errorf("请稍后重试")
	}
	currentIntegral, err := this.GetCurrentIntegral(ctx,&pb.Uid{Uid:uid})
	if utils.CheckErr(err) {
		tx.Rollback()
		return nil, err
	}
	curr_amount := currentIntegral.GetTotalAmount()
	if decimal.NewFromFloat32(curr_amount).LessThan(decimal.NewFromFloat32(req.GetAmount())) {
		tx.Rollback()
		return nil, fmt.Errorf("积分不足，请通过积分充值或参加活动赚取积分")
	}
	res_mount, ok := decimal.NewFromFloat32(curr_amount).Sub(decimal.NewFromFloat(math.Abs(float64(req.GetAmount())))).Float64()
	if !ok {
		tx.Rollback()
		return nil, fmt.Errorf("decimal to float类型转换错误")
	}
	integral := new(entitys.Integral)
	integral.Uid = int(uid)
	integral.ChangeAmount = math.Abs(float64(req.Amount)) * -1
	integral.TotalAmount = res_mount
	mark := "积分消费"
	if req.GetMark() != "" {
		mark = req.GetMark()
	}
	integral.Mark = mark
	integral.Status = 1
	integral.TradeNo = fmt.Sprintf("%v",time.Now().UnixNano())
	integral.CreatedTimestamp = time.Now().Unix()
	if err := tx.Table(integral.TableName()).Create(integral).Error; utils.CheckErr(err) {
		tx.Rollback()
		return nil ,err
	}
	tx.Commit()
	resp = new(pb.FlowRecord)
	db.Table(integral.TableName()).Select("*").Where("id = ?",integral.ID).First(resp)
	return resp, nil
}

var (
	mq = MQ.NewMQ("notify", func(msg []byte) error {
		req := new(pb.NotifyParams)
		err := json.Unmarshal(msg, req)
		if utils.CheckErr(err) {
			return err
		}
		trade_status := req.GetTradeStatus()
		if trade_status != "TRADE_SUCCESS" {
			return fmt.Errorf(trade_status)
		}
		subject := req.GetSubject()
		total_amount := req.GetTotalAmount()
		trade_no := req.GetTradeNo()
		arr := strings.Split(trade_no, "_")
		uid, err := strconv.Atoi(arr[0])
		if utils.CheckErr(err) {
			return err
		}
		out_trade_no := req.GetOutTradeNo()
		db, err := mysqlGORM.GetMysqlDB()
		if utils.CheckErr(err) {
			return err
		}
		tx := db.Begin()
		defer tx.Commit()
		tx.Table(new(entitys.User).TableName()).
			Where("id = ?",uid).
			UpdateColumn("version",gorm.Expr("version + ?",1))
		integral := new(entitys.Integral)
		amount := float64(0)
		if notFound := db.Table(integral.TableName()).
			Select("*").
			Where("uid = ?", uid).
			Where("status = ?", 1).
			Order("id desc").
			First(integral).RecordNotFound(); !notFound {
			amount = integral.TotalAmount
		}
		integral.Uid = uid
		integral.ChangeAmount = float64(total_amount)
		integral.TotalAmount, _ = decimal.NewFromFloat32(total_amount).Add(decimal.NewFromFloat(amount)).Float64()
		integral.Mark = subject
		integral.Status = 1
		integral.OutTradeNo = out_trade_no
		integral.TradeNo = trade_no
		integral.CreatedTimestamp = time.Now().Unix()

		n := 0
		if err = db.Table(integral.TableName()).
			Where("out_trade_no = ?", out_trade_no).
			Where("status = ?", 1).
			Count(&n).Error; utils.CheckErr(err) {
				tx.Rollback()
				return err
		}
		if n > 0 {
			tx.Rollback()
			return fmt.Errorf("该账单已经结算完成")
		}
		if err = tx.Table(integral.TableName()).Create(integral).Error; utils.CheckErr(err) {
			tx.Rollback()
			return err
		}
		return nil
	})
)

func (this *IntegralHdl) Notify(ctx context.Context, req *pb.NotifyParams) (resp *pb.Nil, err error) {
	jn, err := json.Marshal(req)
	if utils.CheckErr(err) {
		return nil, err
	}
	err = mq.Publish(jn)
	if utils.CheckErr(err) {
		return nil, err
	}
	return new(pb.Nil), nil
}

func (this *IntegralHdl) GetCurrentIntegral(ctx context.Context, req *pb.Uid) (resp *pb.CurrentIntegral, err error) {
	uid := req.GetUid()
	if uid == 0 {
		return nil, fmt.Errorf("请传入用户ID")
	}
	db, err := mysqlGORM.GetMysqlDB()
	if utils.CheckErr(err) {
		return nil, err
	}
	last := new(pb.CurrentIntegral)
	last.TotalAmount = 0
	if notFound := db.Table(new(entitys.Integral).TableName()).
		Select("total_amount").
		Where("uid = ?", uid).
		Where("status = ?", 1).
		Order("id desc").
		First(&last).RecordNotFound(); notFound {
		last.TotalAmount = 0.0
	}
	return last, nil
}

func (this *IntegralHdl) GetFlowRecord(ctx context.Context, req *pb.ReqFlowRecordsParams) (resp *pb.RespFlowRecords, err error) {
	uid := req.GetUid()
	if uid == 0 {
		return nil, fmt.Errorf("请传入用户ID")
	}
	db, err := mysqlGORM.GetMysqlDB()
	if utils.CheckErr(err) {
		return nil, err
	}
	sql := db.Table(new(entitys.Integral).TableName()).
		Select("id,change_amount,total_amount,mark,created_timestamp").
		Where("uid = ?", uid).Where("status = ?", 1)
	if req.GetRecharge() {
		sql = sql.Where("change_amount > ?", 0)
	} else if req.GetPay() {
		sql = sql.Where("change_amount < ?", 0)
	}

	if req.GetLastId() > 0 {
		sql = sql.Where("id < ?", req.GetLastId())
	}

	limit := int64(20)
	if req.GetLimit() > 0 {
		limit = req.GetLimit()
	}
	datas := make([]*pb.FlowRecord, 0)
	count := int64(0)
	if err = sql.Count(&count).
		Order("id desc").
		Limit(limit).Offset(0).
		Scan(&datas).Error; utils.CheckErr(err) {
		return nil, err
	}
	return &pb.RespFlowRecords{Count: count, FlowRecords: datas}, nil
}
